﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Timers;
using System.IO.Ports;
using Bentley.Geometry;
using Bentley.GenerativeComponents;
using Bentley.GenerativeComponents.Transactions;
using Bentley.GenerativeComponents.Features;
using Bentley.GenerativeComponents.Features.Specific;
using Bentley.GenerativeComponents.Features.FeatureTypeGenerator;
using Bentley.GenerativeComponents.MicroStation;
using Bentley.Interop.MicroStationDGN;
using Bentley.GenerativeComponents.GCScript;
using Bentley.GenerativeComponents.GeneralPurpose;
using Bentley.GenerativeComponents.Transactions.GraphChange;
using Bentley.GenerativeComponents.Transactions.FeatureTypeGenerator;
using Bentley.GenerativeComponents.Transactions.IncludeFile;
using Bentley.GenerativeComponents.Transactions.Script;
using PubSubSubscriber;

namespace Bentley.GenerativeComponents.Features
{
    public partial class GC_IO_UIv08 : Form
    {
        //private GraphVariable gvRoll;
        private Subscriber sub;        
        private string msgReceived;
        private Dictionary<string, GraphVariable> gvList;
        //a list of subscribed topic as key and the associated graph variable name
        private Dictionary<string, string> topicGVPairList = new Dictionary<string,string>();
        private delegate void SetTextBoxCallback(string text, TextBox txtbox);
        private delegate void SetListBoxCallback(string text, ListBox listbox);
        private bool isPointList = false;

        public GC_IO_UIv08(Dictionary<string, GraphVariable> GraphVariableList)
        {
            #if DEBUG
            TextWriterTraceListener tr = new TextWriterTraceListener(System.IO.File.CreateText
               ("DebugLog\\" + System.DateTime.Now.ToString("yyMd_HHmm") + "_GC_IO_UIv08.txt"));
            Debug.Listeners.Add(tr);
            #endif

            InitializeComponent();
            sub = new Subscriber(txtSubIPAdd.Text, int.Parse(txtSubPortNo.Text));
            sub.messageChanged += UpdateMessage;
            
            //gvRoll = graphVarRoll;
            gvList = GraphVariableList;
            foreach (KeyValuePair<string, GraphVariable> aVar in gvList)
            {
                lstGraphVariables.Items.Add(aVar.Key);
            }
        }

        private void btnSubscribe_Click(object sender, EventArgs e)
        {
            if (lstGraphVariables.SelectedItem == null || lstGraphVariables.SelectedItems.Count == 0)
            {
                MessageBox.Show("Please select a graph variable name in the list box");
            }
            else
            {
                bool subSuccess = Subscribe(txtGVSubscribe.Text, txtTopicName.Text, chkIsPtList.Checked);
                Debug.WriteLine("Subscribed success: " + subSuccess);
                Debug.Flush();
            }
        }

        private void btnUnsubscribe_Click(object sender, EventArgs e)
        {
            if (txtGVSubscribe.Text != null || txtGVSubscribe.Text != "")
            {
                bool unsubSuccess = Unsubscribe(txtGVSubscribe.Text);
                Debug.WriteLine("Unsubscribed success: " + unsubSuccess);
                Debug.Flush();
            }
            else if (lstGraphVariables.SelectedItem != null || lstGraphVariables.SelectedItems.Count != 0)
            {
                bool unsubSuccess = Unsubscribe(lstGraphVariables.SelectedItem.ToString());
                Debug.WriteLine("Unsubscribed success: " + unsubSuccess);
                Debug.Flush();
            }
        }

        public bool Subscribe(string topicNamePublished, string GCVarNameSubscribed, bool isPointList)
        {
            bool subscribed = false;
            this.isPointList = isPointList;
            if (!topicGVPairList.ContainsKey(topicNamePublished))
            {
                sub.Subscribe(topicNamePublished);

                topicGVPairList.Add(topicNamePublished.Trim(), GCVarNameSubscribed);

                if (this.lstTopic.InvokeRequired)
                {
                    SetListBoxCallback st = new SetListBoxCallback(SetListBox);
                    this.Invoke(st, new object[] { topicNamePublished, this.lstTopic });
                }
                else
                {
                    lstGCVarTopicPairs.Items.Add(topicNamePublished + "-" + GCVarNameSubscribed);
                    //lstTopic.Items.Add(topicNamePublished); TODO: add all existing published topics to lstTopic on the top
                }


                Debug.WriteLine("Subscribed: " + topicNamePublished);
                Debug.Flush();
                subscribed = true;
            }
            return subscribed;
        }

        public bool Unsubscribe(string topicName) //TODO: change this to unsubscribe from selection in lstGCVarTopicPairs
        {
            bool unsub = false;
            if (topicGVPairList.ContainsKey(txtGVSubscribe.Text))
            {
                sub.Unsubscribe(txtGVSubscribe.Text);
                topicGVPairList.Remove(txtGVSubscribe.Text);
                lstTopic.Items.Remove(txtGVSubscribe.Text);
                Debug.WriteLine("Unsubscribed: " + txtGVSubscribe.Text);
                Debug.Flush();
            }
            return unsub;
        }

        private void UpdateMessage(object sender, MessageChangedArgs args)
        {
            try
            {
                msgReceived = args.message;

                Debug.WriteLine("message received: " + msgReceived);
                Debug.Flush();                           

                if (this.txtInMessages.InvokeRequired)
                {
                    SetTextBoxCallback st = new SetTextBoxCallback(SetTextBox);
                    this.Invoke(st, new object[] { msgReceived, this.txtInMessages });
                }
                else
                {
                    this.txtInMessages.AppendText(msgReceived + Environment.NewLine); 
                }

                Debug.WriteLine("message appended in text box: " + msgReceived);
                Debug.Flush();

                string[] str = msgReceived.Trim().Split(',');

                if (str[0].Trim().Contains("WII|Roll"))
                {
                    Debug.WriteLine("updating roll");
                    Debug.Flush();

                    double roll = Convert.ToDouble(str[1].Trim());
                    //double change = Math.Abs(Math.Abs(roll) - Math.Abs(prevRoll));
                    //if (change > rollThreshold)
                    //{                
                    if (roll < 0)
                    {
                        roll = 360 + roll;
                    }
                    ModifyGraphVar(gvList[topicGVPairList["WII|Roll"]], roll);
                    //prevRoll = roll;
                    //}
                }
                else
                {
                    if (isPointList && (str[1] != null && str[1] != ""))
                    {                       
                        string s = str[1].Trim();
                        
                        //string format: "{0:1:2;3:4:5;}"
                        // remove {}
                        s = s.Substring(1);
                        s = s.Substring(0, (s.Length - 1));

                        string[] pts = s.Split(';');
                        double[][] ptList = new double[pts.Length][];

                        if (pts[pts.Length -1] == "")
                        {
                           ptList = new double[pts.Length - 1][];                        
                        }
                        
                        for (int i = 0; i < ptList.Length; i++)
                        {
                            string[] xyz = pts[i].Split(':');
                            if (xyz.Length == 3)
                            {
                                try
                                {
                                    ptList[i] = new double[3];
                                    ptList[i][0] = Convert.ToDouble(xyz[0]);
                                    ptList[i][1] = Convert.ToDouble(xyz[1]);
                                    ptList[i][2] = Convert.ToDouble(xyz[2]);
                                }
                                catch (Exception castex)
                                {
                                    MessageBox.Show("Exception in converting messages to point list: you can only send numbers" + Environment.NewLine + castex.Message);
                                    
                                }
                            }
                            else
                            {
                                MessageBox.Show("Point list may contain a point without complete XYZ information");
                            }
                        }
                        
                        ModifyGraphVar(gvList[topicGVPairList[str[0]]], ptList); //change to point arrays
                    }
                    else
                    {
                        ModifyGraphVar(gvList[topicGVPairList[str[0]]], Convert.ToDouble(str[1].Trim()));
                    }
                }
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
                Debug.WriteLine(e.StackTrace);
            }
        }

        /// <summary>
        /// Modify the value of a GraphVariable to the supplied value
        /// </summary>
        /// <param name="aGraphVar">The GraphVariable object</param>
        /// <param name="newValue">The new value to apply</param>
        private void ModifyGraphVar(GraphVariable aGraphVar, double newValue)
        {
            try
            {
                Debug.WriteLine("Try to modify GraphVar: " + aGraphVar.Name + " with value: " + newValue.ToString());
                Debug.Flush();

                IGCObjectContainer graphVarContainer;

                // Casting the GraphVariable to an IValueContainer
                graphVarContainer = (IGCObjectContainer)aGraphVar;
                Debug.WriteLine("Get graphContainer");
                Debug.Flush();

                // and change it's value
                graphVarContainer.SetValue(Boxer.Box(newValue));

                Debug.WriteLine("new value is set to graphContainer");
                Debug.Flush();

                //aGraphVar.GraphDomain.Select();
                //GCTools.UpdateSelectedGraphDomain(false, true);

                GCTools.UpdateAllGraphsInAllGraphDomains(false);
                Debug.WriteLine("updated all graphs");
                Debug.Flush();

                //GraphDomain graphDomain = GCTools.ActiveGraphDomain;
                //IGCEnvironment environment = graphDomain.SafeGCEnvironment();
                //INameCatalogProvider nameCatalogProvider = graphDomain;

                //GCTools.UpdateSingleFeature(environment, nameCatalogProvider, Feature feature);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
                Debug.WriteLine(e.StackTrace);
            }            
        }

        /// <summary>
        /// Modify the value of a GraphVariable to the supplied value
        /// </summary>
        /// <param name="aGraphVar">The GraphVariable object</param>
        /// <param name="newValue">The new value to apply</param>
        private void ModifyGraphVar(GraphVariable aGraphVar, double[][] pointArray)
        {
            try
            {
                Debug.WriteLine("Try to modify GraphVar: " + aGraphVar.Name + " with value: " + pointArray.ToString());
                Debug.Flush();

                IGCObjectContainer graphVarContainer;

                // Casting the GraphVariable to an IValueContainer
                graphVarContainer = (IGCObjectContainer)aGraphVar;
                Debug.WriteLine("Get graphContainer");
                Debug.Flush();

                // and change it's value
                graphVarContainer.SetValue(Boxer.Box(pointArray));

                Debug.WriteLine("new value is set to graphContainer");
                Debug.Flush();

                //aGraphVar.GraphDomain.Select();
                //GCTools.UpdateSelectedGraphDomain(false, true);

                GCTools.UpdateAllGraphsInAllGraphDomains(false);
                Debug.WriteLine("updated all graphs");
                Debug.Flush();

                //GraphDomain graphDomain = GCTools.ActiveGraphDomain;
                //IGCEnvironment environment = graphDomain.SafeGCEnvironment();
                //INameCatalogProvider nameCatalogProvider = graphDomain;

                //GCTools.UpdateSingleFeature(environment, nameCatalogProvider, Feature feature);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
                Debug.WriteLine(e.StackTrace);
            }
        }

        // This method is passed in to the SetTextCallBack delegate to set the Text property of Label lbl
        private void SetTextBox(string text, TextBox txtbox)
        {
            txtbox.AppendText(text + Environment.NewLine);            
        }

        // This method is passed in to the SetTextCallBack delegate to set the Text property of Label lbl
        private void SetListBox(string text, ListBox listbox)
        {
            lstTopic.Items.Add(text);
        }
        
        public void DisconnectFromUbimash()
        {
            sub.Shutdown();
            Debug.WriteLine("Subscriber shut down");
            Debug.Flush();
        }

        private void lstGraphVariables_SelectedIndexChanged(object sender, EventArgs e)
        {
            txtGVSubscribe.Text = lstGraphVariables.SelectedIndex.ToString();
        }

        private void lstTopic_SelectedIndexChanged(object sender, EventArgs e)
        {
            txtTopicName.Text = lstTopic.SelectedIndex.ToString();
        }

    }
}
